#include "Level.h"
#include <fstream>
#include <iostream>

Level::Level(std::string fileName) : lvlState(STATE::START), levelComplete(false)
{
    frameAni = thor::FrameAnimation::Create();
    for(int i = 0; i < 1024; i+=128)
    {
        for(int j = 0; j < 1024; j+=128)
        {
            frameAni->AddFrame(1.f, sf::IntRect(j, i, 128, 128));
        }
    }
    load(fileName);
    panel.setPosition(WIDTH/2, HEIGHT-16);
    balls.push_back(new Ball);
    endText.SetString("Level completed! Press right mouse button to go to the next level!");
    endText.SetCharacterSize(20);
    setCenter(endText, 200);
    endText.Move(-100, 0);
}

void Level::addBall()
{
    Ball* ball = new Ball;
    ball->ballSprite.SetPosition(820, 450);
    ball->dx = -2;
    ball->start();
    balls.push_back(ball);
}

void Level::reset()
{
    for(auto itr = bricks.begin(); itr != bricks.end(); itr++)
        (*itr)->destroyed = false;
    for(auto itr = balls.begin(); itr != balls.end(); itr++)
    {
        delete (*itr);
        itr = balls.erase(itr);
    }
    bonuses.clear();
    levelComplete = false;
}

void Level::load(std::string fileName)
{
    amountOfBricks = 0;

    TiXmlDocument levelFile(fileName.c_str());
    if (!levelFile.LoadFile())
    {
        std::cout << "Loading level \"" << fileName << "\" failed." << std::endl;
        return;
    }

    TiXmlElement *map;
    map = levelFile.FirstChildElement("Level");

    TiXmlElement *layers;
    layers = map->FirstChildElement("Layers");

    TiXmlElement *layer;
    layer = layers->FirstChildElement("Layer");

    TiXmlElement *items;
    items = layer->FirstChildElement("Items");

    TiXmlElement *item;
    item = items->FirstChildElement("Item");

    while(item != NULL)
    {
        TiXmlElement *texture;
        texture = item->FirstChildElement("texture_filename");
        sf::Sprite sprite;
        std::string textureName = texture->GetText();
        sprite.SetTexture(rm->getTexture(texture->GetText()));

        TiXmlElement *position;
        position = item->FirstChildElement("Position");

        TiXmlElement *X;
        X = position->FirstChildElement("X");
        int x = atoi(X->GetText());

        TiXmlElement *Y;
        Y = position->FirstChildElement("Y");
        int y = atoi(Y->GetText());

        sprite.SetPosition(x - sprite.GetGlobalBounds().Width/2, y - sprite.GetGlobalBounds().Height/2);

        properties props;
        TiXmlElement *propElement;
        propElement = item->FirstChildElement("CustomProperties");

        TiXmlElement *prop;
        prop = propElement->FirstChildElement("Property");
        while(prop != NULL)
        {
            std::string propName = prop->Attribute("Name");
            if(propName == "moving")
                props.moving = true;

            prop = prop->NextSiblingElement("Property");
        }
        if(textureName == "greenBrick.png")
            props.hitTwice = true;
        else if(textureName == "explosiveBrick.png")
            props.explosive = true;
        else if(textureName == "blackBrick.png")
            props.stone = true;
        else if(textureName == "pinBrick.png")
            props.pin = true;

        Brick* temp = new Brick(sprite, props);
        bricks.push_back(temp);

        if(!props.stone)
            amountOfBricks++;

        item = item->NextSiblingElement("Item");
    }
    return;
}

void Level::update(int x)
{
    //panel
    panel.update(x);

    //balls
    for(auto itr = balls.begin(); itr != balls.end(); itr++)
    {
        (*itr)->update();
        if((*itr)->destroyed)
        {
            delete (*itr);
            itr = balls.erase(itr);
            onBallMissed();
        }
    }

    //bonuses
    for(auto itr = bonuses.begin(); itr != bonuses.end(); itr++)
    {
        (*itr)->update();
        if((*itr)->destroyed)
        {
            delete (*itr);
            itr = bonuses.erase(itr);
        }
    }

    //sounds
    rm->releaseStoppedSounds();

    //collisions
    collisionUpdate();

    //level complete?
    if(amountOfBricks <= 0 && lvlState == STATE::PLAYING)
        onLevelCompleted();
}

void Level::onLevelCompleted()
{
    for(auto itr = balls.begin(); itr != balls.end(); itr++)
        (*itr)->pause();
    if(applause)
        rm->playSound("clap.wav");
    lvlState = STATE::END;
}

void Level::onBallMissed()
{
    if(balls.size() == 0)
    {
        lvlState = STATE::START;
        lives--;
        tLivesLost++;
        balls.push_back(new Ball);
    }
}

void Level::addExplosion(int x, int y)
{
    animations.push_back(new Animation(frameAni, 1000.0, x-40, y-40));
}

void Level::randomBonus(int x, int y)
{
    int num = rand() % 150;
    if(num < 10)
        bonuses.push_back(new Bonus(x, y, Bonus::SMALLERPANEL));
    else if(num < 20)
        bonuses.push_back(new Bonus(x, y, Bonus::BIGGERPANEL));
    else if(num < 24)
        bonuses.push_back(new Bonus(x, y, Bonus::LIVE));
    else if(num < 30)
        bonuses.push_back(new Bonus(x, y, Bonus::ACID));
    else if(num < 35)
        bonuses.push_back(new Bonus(x, y, Bonus::PAUSE));
    else if(num < 40)
        bonuses.push_back(new Bonus(x, y, Bonus::BALL));
}

void Level::collisionUpdate()
{
    //balls with panel
    for(auto itr = balls.begin(); itr != balls.end(); itr++)
        if(collide(panel.panelSprite, (*itr)->ballSprite))
        {
            float distance = (*itr)->ballSprite.GetGlobalBounds().Left + (*itr)->ballSprite.GetGlobalBounds().Width/2 - (panel.panelSprite.GetGlobalBounds().Left + panel.panelSprite.GetGlobalBounds().Width/2);
            float newx = distance / 15.f;

            (*itr)->dx = newx;
            (*itr)->dy = -abso((*itr)->dy);
            rm->playSound("tap.wav");
        }

    //bricks with ball
    for(auto itr = bricks.begin(); itr != bricks.end(); itr++)
    {
        (*itr)->update(this);

        if((*itr)->destroyed)
        {
            itr = bricks.erase(itr);
            amountOfBricks--;
        }
    }

    //bonuses with panel
    for(auto itr = bonuses.begin(); itr != bonuses.end(); itr++)
    {
        if(collide(panel.panelSprite, (*itr)->bonusSprite))
        {
            (*itr)->onCatch(this);
            itr = bonuses.erase(itr);
        }
    }
}

void Level::draw(sf::RenderWindow* Window)
{
    //draw bricks
    for(auto itr = bricks.begin(); itr != bricks.end(); itr++)
        (*itr)->draw(Window);

    //draw bonuses
    for(auto itr = bonuses.begin(); itr != bonuses.end(); itr++)
        (*itr)->draw(Window);

    //draw explosions
    for(auto itr = animations.begin(); itr != animations.end(); itr++)
        (*itr)->update(Window);

    //draw balls
    for(auto itr = balls.begin(); itr != balls.end(); itr++)
        (*itr)->draw(Window);

    panel.draw(Window);
    if(lvlState == STATE::START)
        Window->Draw(startText);
    else if(lvlState == STATE::END)
        Window->Draw(endText);
}

void Level::mousePressed()
{
    if(lvlState == STATE::START)
    {
        lvlState = STATE::PLAYING;
        for(auto itr = balls.begin(); itr != balls.end(); itr++)
            (*itr)->start();
    }
    else if(lvlState == STATE::PLAYING)
    {
        if(!paused)
        {
            if(pauseCount > 0)
            {
                --pauseCount;
                paused = true;
                for(auto itr = balls.begin(); itr != balls.end(); itr++)
                    (*itr)->pause();

            }
        }
        else
        {
            for(auto itr = balls.begin(); itr != balls.end(); itr++)
                (*itr)->start();
            paused = false;
        }
    }
    else if(lvlState == STATE::END)
    {
        levelComplete = true;
    }
}

Level::~Level()
{
    for(auto itr = bricks.begin(); itr != bricks.end(); itr++)
        delete (*itr);
    bricks.clear();

    for(auto itr = balls.begin(); itr != balls.end(); itr++)
        delete (*itr);
    balls.clear();

    for(auto itr = bonuses.begin(); itr != bonuses.end(); itr++)
        delete (*itr);
    bonuses.clear();

    for(auto itr = animations.begin(); itr != animations.end(); itr++)
        delete (*itr);
    animations.clear();
}
